#ifndef _ULIB_H_
#define _ULIB_H_

#include <error.h>
#include <types.h>
#include <syscall.h>
#include <trap.h>
#include <stdarg.h>
#include <x86.h>
#include <layout.h>
#include <mmu.h>
#include <x86_lock.h>
#include <proc.h>
#include <sysconf.h>

#include <string.h>
#include <ioif.h>
#include <parse.h>

#include "ulayout.h"
#include "va.h"
#include "ips.h"
#include "ipc.h"
#include "process.h"
#include "tb.h"
#include "pcireg.h"
#include "pci.h"
#include "file.h"
#include "fs.h"

/* INTERFACE WITH KERNEL ====================================== */

/* The following is defined in ulib.S */
static __inline int syscall0(uint32_t num) __attribute__((always_inline));
static __inline int syscall1(uint32_t num, uint32_t a) __attribute__((always_inline));
static __inline int syscall2(uint32_t num, uint32_t a, uint32_t b) __attribute__((always_inline));
static __inline int syscall3(uint32_t num, uint32_t a, uint32_t b, uint32_t c) __attribute__((always_inline));
static __inline int syscall4(uint32_t num, uint32_t a, uint32_t b, uint32_t c, uint32_t d) __attribute__((always_inline));
static __inline int syscall5(uint32_t num, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e) __attribute__((always_inline));

static __inline int
syscall0(uint32_t num)
{
	 __asm __volatile("int %0"
					  : 
					  : "n"(INT_SYSCALL), "a"(num));
}

static __inline int
syscall1(uint32_t num, uint32_t a)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_SYSCALL), "a"(num), "b"(a));
}

static __inline int
syscall2(uint32_t num, uint32_t a, uint32_t b)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_SYSCALL), "a"(num), "b"(a), "c"(b));
}

static __inline int
syscall3(uint32_t num, uint32_t a, uint32_t b, uint32_t c)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_SYSCALL), "a"(num), "b"(a), "c"(b), "d"(c));
}

static __inline int
syscall4(uint32_t num, uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_SYSCALL), "a"(num), "b"(a), "c"(b), "d"(c), "S"(d));
}

static __inline int
syscall5(uint32_t num, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_SYSCALL), "a"(num), "b"(a), "c"(b), "d"(c), "S"(d), "D"(e));
}

__attribute__((noreturn)) void kcb_ret(uint32_t ret);

/* alias of system call */
static inline int sc_fork_proc(void *cb, uintptr_t stack)
{ return syscall2(SC_FORK_PROC, (uint32_t)cb, (uint32_t)stack); }
static inline int sc_fork_mem(void)
{ return syscall0(SC_FORK_MEM); }

static inline void
sc_kprintf(const char *fmt, ...)
{
     va_list va;
     va_start(va, fmt);
     syscall2(SC_KPRINTF, (uint32_t)fmt, (uint32_t)va);
     va_end(va);
}

static inline void
sc_wait(spin_lock_t *lock)
{
	 syscall1(SC_WAIT, (uint32_t)lock);
}

static inline void
sc_notify(proc_id_t pid)
{
	 syscall1(SC_NOTIFY, (uint32_t)pid);
}

static inline int
sc_mark_shared_4M(void *addr)
{
	 return syscall1(SC_MARK_SHARED,
					 UVPT + ((uint32_t)PAGEDIR_IDX(addr) << PAGE_SHIFT));
}

static inline int
sc_mark_shared_4K(void *addr)
{
	 return syscall1(SC_MARK_SHARED, (uint32_t)addr);
}

static inline int
sc_alloc_temp_4M(void *addr)
{
	 return syscall1(SC_ALLOC_TEMP,
					 UVPT + ((uint32_t)PAGEDIR_IDX(addr) << PAGE_SHIFT));
}

static inline int
sc_alloc_temp_4K(void *addr)
{
	 return syscall1(SC_ALLOC_TEMP, (uint32_t)addr);
}

static inline physaddr_t
sc_alloc_io(void)
{
	 return syscall0(SC_ALLOC_IO);
}

static inline int
sc_set_signal_handler(void *handler)
{
	 return syscall1(SC_SET_SH, (uint32_t)handler);
}

static inline int
sc_set_signal_flags(uint16_t flags, uint16_t sch_count)
{
	 return syscall2(SC_SET_SF, (uint32_t)flags, (uint32_t)sch_count);
}

static inline int
sc_irq_signal(int irq, proc_id_t pid)
{
	 return syscall2(SC_IRQ_SIGNAL, (uint32_t)irq, (uint32_t)pid);
}

static inline int
sc_spl_acquire(spin_lock_t *lock)
{
	 return syscall1(SC_SPL_ACQUIRE, (uint32_t)lock);
}

static inline int
sc_map_io(void *addr, physaddr_t page)
{
	 return syscall2(SC_MAP_IO, (uint32_t)addr, (uint32_t)page);
}

extern ptab_entry_t vpt[];
extern pdir_entry_t vpd[];
extern struct proc_t procs[];
extern struct sysconf_t sysconf;

/* Read-only LAPIC access */
extern uint32_t lapic[];
// Local APIC registers, divided by 4 for use as uint[] indices.
#define ID      (0x0020/4)   // ID
#define VER     (0x0030/4)   // Version
#define TPR     (0x0080/4)   // Task Priority
#define EOI     (0x00B0/4)   // EOI
#define SVR     (0x00F0/4)   // Spurious Interrupt Vector
  #define ENABLE     0x00000100   // Unit Enable
#define ESR     (0x0280/4)   // Error Status
#define ICRLO   (0x0300/4)   // Interrupt Command
  #define INIT       0x00000500   // INIT/RESET
  #define STARTUP    0x00000600   // Startup IPI
  #define DELIVS     0x00001000   // Delivery status
  #define ASSERT     0x00004000   // Assert interrupt (vs deassert)
  #define LEVEL      0x00008000   // Level triggered
  #define BCAST      0x00080000   // Send to all APICs, including self.
#define ICRHI   (0x0310/4)   // Interrupt Command [63:32]
#define TIMER   (0x0320/4)   // Local Vector Table 0 (TIMER)
  #define X1         0x0000000B   // divide counts by 1
  #define PERIODIC   0x00020000   // Periodic
#define PCINT   (0x0340/4)   // Performance Counter LVT
#define LINT0   (0x0350/4)   // Local Vector Table 1 (LINT0)
#define LINT1   (0x0360/4)   // Local Vector Table 2 (LINT1)
#define ERROR   (0x0370/4)   // Local Vector Table 3 (ERROR)
  #define MASKED     0x00010000   // Interrupt masked
#define TICR    (0x0380/4)   // Timer Initial Count
#define TCCR    (0x0390/4)   // Timer Current Count
#define TDCR    (0x03E0/4)   // Timer Divide Configuration


static inline int
cur_cpu(void)
{
	 return lapic[ID];
}

static inline int
cur_pid(void)
{
	 volatile struct taskstate_t *task =
		  (volatile struct taskstate_t *)(UTASK + (cur_cpu() << TS_USHIFT));
	 return ((task->esp0 ^ KSTACK) - 1) >> KSTACK_SHIFT;
}

/* USER LIB =================================================== */

#endif /* _ULIB_H_ */
